/*!
 * \file WtBtDtReader.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief WonderTrader回测数据读取器实现文件
 * 
 * \details 该文件实现了WtBtDtReader回测数据读取器类的所有方法，包括：
 *          - 初始化和配置管理
 *          - 原始K线数据的文件读取和处理
 *          - 原始Tick数据的文件读取和处理
 *          - 数据文件路径构建和验证
 *          - 数据压缩格式的解析和处理
 *          - 完整的错误处理和日志记录
 */
#include "WtBtDtReader.h"

#include "../Includes/WTSVariant.hpp"
#include "../Share/StrUtil.hpp"
#include "../WTSUtils/WTSCmpHelper.hpp"

//By Wesley @ 2022.01.05
#include "../Share/fmtlib.h"

/*!
 * \brief 回测数据读取器日志输出函数模板
 * \tparam Args 参数类型模板
 * \param sink 回测数据读取器回调接口
 * \param ll 日志级别
 * \param format 格式化字符串
 * \param args 可变参数
 * 
 * 线程安全的日志输出函数，使用线程局部存储的缓冲区
 */
template<typename... Args>
inline void pipe_btreader_log(IBtDtReaderSink* sink, WTSLogLevel ll, const char* format, const Args&... args)
{
	if (sink == NULL)
		return;

	static thread_local char buffer[512] = { 0 };
	memset(buffer, 0, 512);
	fmt::format_to(buffer, format, args...);

	sink->reader_log(ll, buffer);
}

/*!
 * \brief 外部声明：处理数据块的压缩和版本兼容性
 * \param content 数据内容引用
 * \param isBar 是否为K线数据
 * \param bKeepHead 是否保留数据块头部，默认为true
 * \return 处理是否成功
 * 
 * 外部函数声明，用于处理数据块的压缩、解压缩和版本转换
 */
extern bool proc_block_data(std::string& content, bool isBar, bool bKeepHead = true);

/*!
 * \brief C接口导出函数：创建回测数据读取器实例
 * \return 回测数据读取器接口指针
 * 
 * 动态库导出函数，用于创建WtBtDtReader实例
 */
extern "C"
{
	EXPORT_FLAG IBtDtReader* createBtDtReader()
	{
		IBtDtReader* ret = new WtBtDtReader();
		return ret;
	}

	/*!
	 * \brief C接口导出函数：删除回测数据读取器实例
	 * \param reader 回测数据读取器接口指针
	 * 
	 * 动态库导出函数，用于安全删除WtBtDtReader实例
	 */
	EXPORT_FLAG void deleteBtDtReader(IBtDtReader* reader)
	{
		if (reader != NULL)
			delete reader;
	}
};

/*!
 * \brief 外部函数声明：数据块处理
 * \param content 数据内容引用
 * \param isBar 是否为K线数据
 * \param bKeepHead 是否保留头部信息
 * \return 处理是否成功
 * 
 * 处理数据块的压缩格式转换和版本兼容性
 */
extern bool proc_block_data(std::string& content, bool isBar, bool bKeepHead);

/*!
 * \brief 构造函数
 * 
 * 初始化回测数据读取器实例，设置默认参数
 */
WtBtDtReader::WtBtDtReader()
{
}

/*!
 * \brief 析构函数
 * 
 * 清理资源和释放内存
 */
WtBtDtReader::~WtBtDtReader()
{

}

/*!
 * \brief 初始化回测数据读取器
 * \param cfg 配置参数对象
 * \param sink 回调接口指针
 * 
 * 根据配置参数初始化回测数据读取器，设置数据根目录路径
 */
void WtBtDtReader::init(WTSVariant* cfg, IBtDtReaderSink* sink)
{
	_sink = sink;

	if (cfg == NULL)
		return;

	_base_dir = cfg->getCString("path");
	_base_dir = StrUtil::standardisePath(_base_dir);

	pipe_btreader_log(_sink, LL_INFO, "WtBtDtReader initialized, root data dir is {}", _base_dir);
}

/*!
 * \brief 读取原始K线数据
 * \param exchg 交易所代码
 * \param code 合约代码
 * \param period K线周期类型
 * \param buffer 输出数据缓冲区
 * \return 是否读取成功
 * 
 * 从磁盘文件读取指定合约的K线数据，支持多种周期类型
 */
bool WtBtDtReader::read_raw_bars(const char* exchg, const char* code, WTSKlinePeriod period, std::string& buffer)
{
	std::stringstream ss;
	ss << _base_dir << "his/" << PERIOD_NAME[period] << "/" << exchg << "/" << code << ".dsb";
	std::string filename = ss.str();
	if (!StdFile::exists(filename.c_str()))
	{
		pipe_btreader_log(_sink, LL_WARN, "Back {} data file {} not exists", PERIOD_NAME[period], filename);
		return false;
	}

	pipe_btreader_log(_sink, LL_DEBUG, "Reading back {} bars from file {}...", PERIOD_NAME[period], filename);
	StdFile::read_file_content(filename.c_str(), buffer);
	bool bSucc = proc_block_data(buffer, true, false);
	if(!bSucc)
		pipe_btreader_log(_sink, LL_ERROR, "Processing back {} data from file {} failed", PERIOD_NAME[period], filename);

	return bSucc;
}

/*!
 * \brief 读取原始Tick数据
 * \param exchg 交易所代码
 * \param code 合约代码
 * \param uDate 交易日期（YYYYMMDD格式）
 * \param buffer 输出数据缓冲区
 * \return 是否读取成功
 * 
 * 从磁盘文件读取指定合约指定日期的Tick数据
 */
bool WtBtDtReader::read_raw_ticks(const char* exchg, const char* code, uint32_t uDate, std::string& buffer)
{
	std::stringstream ss;
	ss << _base_dir << "his/ticks/" << exchg << "/" << uDate << "/" << code << ".dsb";
	std::string filename = ss.str();
	if (!StdFile::exists(filename.c_str()))
	{
		pipe_btreader_log(_sink, LL_WARN, "Back tick data file {} not exists", filename);
		return false;
	}

	StdFile::read_file_content(filename.c_str(), buffer);
	bool bSucc = proc_block_data(buffer, false, false);
	if (!bSucc)
		pipe_btreader_log(_sink, LL_ERROR, "Processing back tick data from file {} failed", filename);

	return bSucc;
}